package com.ziroom.crm.mq.core.config;

import com.ziroom.crm.mq.core.logging.Log;
import com.ziroom.crm.mq.core.logging.LogFactory;
import com.ziroom.crm.mq.core.persistence.Persistence;
import com.ziroom.crm.mq.core.retry.RetryConst;
import com.ziroom.crm.mq.core.util.AssertUtil;
import org.apache.commons.lang3.StringUtils;

import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 全局配置体系(如果可以，保证所有的配置都有默认值)
 * mq： 全局配置根节点
 * -spi： spi相关配置
 * -persistence： 持久化相关配置
 * -statistic 统计相关配置
 * -retry 重试相关配置
 * <p>
 * <PRE>
 * <BR>    修改记录
 * <BR>-----------------------------------------------
 * <BR>    修改日期         修改人          修改内容
 * </PRE>
 *
 * @author WeiShaoying
 * @version 1.0
 * @date Created in 2020年08月28日 10:46
 * @since 1.0
 */
@SuppressWarnings("ALL")
public class MqConfig {

    private static final Log log = LogFactory.getLog(MqConfig.class);

    private static final Map<String, String> props = new ConcurrentHashMap<>();

    private static final String CHARSET = "crm.mq.charset";

    /**
     * spi加载时，需要排除的class , 可以多个, 逗号分隔
     */
    public static final String SPI_EXCLUDE_CLASS = "mq.spi.exclude.class";

    /**
     * 持久化类型
     */
    public static final String PERSISTENCE_TYPE = "mq.persistence.type";

    /**
     * 持久化文件保存路径
     */
    public static final String PERSISTENCE_FILE_PATH = "mq.persistence.file.path";

    /**
     * 是否开启内部定时配置 true 表示开启
     */
    public static final String INNER_JOB_ENABLE = "mq.inner.job.enable";

    /**
     * 是否开启统计 true表示开启
     */
    public static final String STATISTIC_ENABLE = "mq.statistic.enable";

    /**
     * 发送端持久化重试线程，重试间隔时长
     */
    public static final String RESEND_THREAD_SLEEP_MILLI = "mq.resend.thread.sleep.milli";

    /**
     * 发送端持久化重试，每次重试数据数量
     */
    public static final String RESEND_FETCH_SIZE = "mq.resend.fetch.size";

    /**
     * 发送端持久化重试，阈值数量
     */
    public static final String RESEND_THRESHOLD = "mq.resend.threshold";

    /**
     * 消费端持久化重试线程，重试间隔时长
     */
    public static final String RECONSUME_THREAD_SLEEP_MILLI = "mq.reconsume.thread.sleep.milli";

    /**
     * 消费端持久化重试，每次重试数据数量
     */
    public static final String RECONSUME_FETCH_SIZE = "mq.reconsume.fetch.size";

    /**
     * 消费端持久化重试，阈值数量
     */
    public static final String RECONSUME_THRESHOLD = "mq.reconsume.threshold";

    /**
     * 立即失败重试
     */
    public static final String RETRY_CONSUME_POLICY = "mq.retry.consume.policy";
    public static final String RETRY_CONSUME_COUNT = "mq.retry.consume.count";
    public static final String RETRY_CONSUME_DELAY_POLICY = "mq.retry.consume.delay.policy";
    public static final String RETRY_CONSUME_DELAY_PERIOD = "mq.retry.consume.delay.period";
    public static final String RETRY_CONSUME_SECOND_BEFORE_RECONSUME = "mq.retry.consume.second.before.reconsume";

    public static final String RETRY_RECONSUME_POLICY = "mq.retry.reconsume.policy";
    public static final String RETRY_RECONSUME_COUNT = "mq.retry.reconsume.count";
    public static final String RETRY_RECONSUME_DELAY_POLICY = "mq.retry.reconsume.delay.policy";
    public static final String RETRY_RECONSUME_DELAY_PERIOD = "mq.retry.reconsume.delay.period";

    public static final String RETRY_SEND_POLICY = "mq.retry.send.policy";
    public static final String RETRY_SEND_COUNT = "mq.retry.send.count";
    public static final String RETRY_SEND_DELAY_POLICY = "mq.retry.send.delay.policy";
    public static final String RETRY_SEND_DELAY_PERIOD = "mq.retry.send.delay.period";
    public static final String RETRY_SEND_SECOND_BEFORE_RESEND = "mq.retry.send.second.before.resend";

    public static final String RETRY_RESEND_POLICY = "mq.retry.resend.policy";
    public static final String RETRY_RESEND_COUNT = "mq.retry.resend.count";
    public static final String RETRY_RESEND_DELAY_POLICY = "mq.retry.resend.delay.policy";
    public static final String RETRY_RESEND_DELAY_PERIOD = "mq.retry.resend.delay.period";

    /*-------------------------下面是默认值 --------------------*/

    /**
     * 默认不排除spi 加载类
     */
    private static final String DEFAULT_SPI_EXCLUDE_CLASS = "";

    /**
     * 默认文件路径
     */
    private static final String DEFAULT_PERSISTENCE_FILE_PATH = "/app/crm-mq";

    /**
     * 开启内部定时 默认为开启
     */
    private static final String DEFAULT_INNER_JOB_ENABLE = "true";

    /**
     * 是否开启统计 默认不开启
     */
    private static final String DEFAULT_STATISTIC_ENABLE = "false";

    /**
     * 发送端持久化重试线程，重试间隔时长,默认5分钟
     */
    private static final String DEFAULT_RESEND_THREAD_SLEEP_MILLI = "300000";
    /**
     * 发送端持久化重试，每次重试数据数量
     */
    private static final String DEFAULT_RESEND_FETCH_SIZE = "500";

    /**
     * 消费端持久化重试线程，重试间隔时长,默认5分钟
     */
    private static final String DEFAULT_RECONSUME_THREAD_SLEEP_MILLI = "300000";
    /**
     * 消费端持久化重试，每次重试数据数量
     */
    private static final String DEFAULT_RECONSUME_FETCH_SIZE = "500";

    /**
     * 立即失败重试次数
     */
    private static final String DEFAULT_RETRY_CONSUME_POLICY = RetryConst.RETRY_POLICY_COUNT;
    private static final String DEFAULT_RETRY_CONSUME_COUNT = "1";
    private static final String DEFAULT_RETRY_CONSUME_DELAY_PERIOD = "2000";
    private static final String DEFAULT_RETRY_CONSUME_SECOND_BEFORE_RECONSUME = "3600";
    private static final String DEFAULT_RETRY_CONSUME_DELAY_POLICY = RetryConst.DELAY_POLICY_FIXED;

    private static final String DEFAULT_RETRY_RECONSUME_POLICY = RetryConst.RETRY_POLICY_COUNT;
    private static final String DEFAULT_RETRY_RECONSUME_DELAY_PERIOD = "2000";
    private static final String DEFAULT_RETRY_RECONSUME_DELAY_POLICY = RetryConst.DELAY_POLICY_EXPONENTIAL;
    private static final String DEFAULT_RETRY_RECONSUME_COUNT = "15";

    private static final String DEFAULT_RETRY_SEND_POLICY = RetryConst.RETRY_POLICY_COUNT;
    private static final String DEFAULT_RETRY_SEND_COUNT = "0"; // 因为内存重试会占用线程,可能对客户端吞吐量造成影响，默认关闭
    private static final String DEFAULT_RETRY_SEND_DELAY_PERIOD = "2000";
    private static final String DEFAULT_RETRY_SEND_SECOND_BEFORE_RESEND = "3600";
    private static final String DEFAULT_RETRY_SEND_DELAY_POLICY = RetryConst.DELAY_POLICY_FIXED;

    private static final String DEFAULT_RETRY_RESEND_POLICY = RetryConst.RETRY_POLICY_COUNT;
    private static final String DEFAULT_RETRY_RESEND_DELAY_PERIOD = "2000";
    private static final String DEFAULT_RETRY_RESEND_DELAY_POLICY = RetryConst.DELAY_POLICY_EXPONENTIAL;
    private static final String DEFAULT_RETRY_RESEND_COUNT = "15";

    private static final String DEFAULT_CHARSET = "UTF-8";

    static {
        try {
            initialize();
            loadProps();
        } catch (Throwable ex) {
            log.warn("[MqConfig] Failed to initialize", ex);
            ex.printStackTrace();
        }
    }

    /**
     * Get config value of the specific key.
     *
     * @param key config key
     * @return the config value.
     */
    public static String getConfig(String key) {
        AssertUtil.notNull(key, "key cannot be null");
        return props.get(key);
    }

    public static void setConfig(String key, String value) {
        AssertUtil.notNull(key, "key cannot be null");
        AssertUtil.notNull(value, "value cannot be null");
        props.put(key, value);
    }

    public static void setConfigOrDefault(String key, String value) {
        AssertUtil.notNull(key, "key cannot be null");
        if (StringUtils.isNotBlank(value)) {
            props.put(key, value);
        }
    }

    public static String removeConfig(String key) {
        AssertUtil.notNull(key, "key cannot be null");
        return props.remove(key);
    }

    public static Integer getConfigInt(String key, Integer defaultValue) {
        String config = MqConfig.getConfig(key);
        if (StringUtils.isBlank(config)) {
            return defaultValue;
        }
        return Integer.valueOf(config);
    }

    private static void initialize() {
        // Init default properties.
        setConfig(CHARSET, DEFAULT_CHARSET);
        setConfig(SPI_EXCLUDE_CLASS, DEFAULT_SPI_EXCLUDE_CLASS);
        setConfig(INNER_JOB_ENABLE, DEFAULT_INNER_JOB_ENABLE);
        setConfig(PERSISTENCE_TYPE, Persistence.FILE);
        setConfig(STATISTIC_ENABLE, DEFAULT_STATISTIC_ENABLE);
        setConfig(PERSISTENCE_FILE_PATH, DEFAULT_PERSISTENCE_FILE_PATH);

        //持久化重试相关配置
        setConfig(RESEND_THREAD_SLEEP_MILLI, DEFAULT_RESEND_THREAD_SLEEP_MILLI);
        setConfig(RESEND_FETCH_SIZE, DEFAULT_RESEND_FETCH_SIZE);
        setConfig(RECONSUME_THREAD_SLEEP_MILLI, DEFAULT_RECONSUME_THREAD_SLEEP_MILLI);
        setConfig(RECONSUME_FETCH_SIZE, DEFAULT_RECONSUME_FETCH_SIZE);

        //retry
        setConfig(RETRY_CONSUME_POLICY, DEFAULT_RETRY_CONSUME_POLICY);
        setConfig(RETRY_CONSUME_COUNT, DEFAULT_RETRY_CONSUME_COUNT);
        setConfig(RETRY_CONSUME_DELAY_POLICY, DEFAULT_RETRY_CONSUME_DELAY_POLICY);
        setConfig(RETRY_CONSUME_DELAY_PERIOD, DEFAULT_RETRY_CONSUME_DELAY_PERIOD);
        setConfig(RETRY_CONSUME_SECOND_BEFORE_RECONSUME, DEFAULT_RETRY_CONSUME_SECOND_BEFORE_RECONSUME);

        setConfig(RETRY_RECONSUME_POLICY, DEFAULT_RETRY_RECONSUME_POLICY);
        setConfig(RETRY_RECONSUME_COUNT, DEFAULT_RETRY_RECONSUME_COUNT);
        setConfig(RETRY_RECONSUME_DELAY_POLICY, DEFAULT_RETRY_RECONSUME_DELAY_POLICY);
        setConfig(RETRY_RECONSUME_DELAY_PERIOD, DEFAULT_RETRY_RECONSUME_DELAY_PERIOD);

        setConfig(RETRY_SEND_POLICY, DEFAULT_RETRY_SEND_POLICY);
        setConfig(RETRY_SEND_COUNT, DEFAULT_RETRY_SEND_COUNT);
        setConfig(RETRY_SEND_DELAY_POLICY, DEFAULT_RETRY_SEND_DELAY_POLICY);
        setConfig(RETRY_SEND_DELAY_PERIOD, DEFAULT_RETRY_SEND_DELAY_PERIOD);
        setConfig(RETRY_SEND_SECOND_BEFORE_RESEND, DEFAULT_RETRY_SEND_SECOND_BEFORE_RESEND);

        setConfig(RETRY_RESEND_POLICY, DEFAULT_RETRY_RESEND_POLICY);
        setConfig(RETRY_RESEND_COUNT, DEFAULT_RETRY_RESEND_COUNT);
        setConfig(RETRY_RESEND_DELAY_POLICY, DEFAULT_RETRY_RESEND_DELAY_POLICY);
        setConfig(RETRY_RESEND_DELAY_PERIOD, DEFAULT_RETRY_RESEND_DELAY_PERIOD);
    }

    private static void loadProps() {
        Properties properties = MqConfigLoader.getProperties();
        for (Object key : properties.keySet()) {
            setConfig((String) key, (String) properties.get(key));
        }
    }

    public static String charset() {
        return props.get(CHARSET);
    }

    private MqConfig() {
    }
}
